package mx.uacam.ermilo.ercu.ui.tablemodel;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.table.AbstractTableModel;

/**
 * 
 * @author Freddy Barrera
 */
public abstract class NewAbstractTableModel<T> extends AbstractTableModel{
    
    private Class<T> t;
    protected List<T> data;
    private List<String> titles;

    public NewAbstractTableModel(T[] data) {
        this(Arrays.asList(data));
    }
    
    public NewAbstractTableModel(List<T> data) {
        this.data = data;
        t = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass())
                .getActualTypeArguments()[0];
        titles = new ArrayList<String>();
        setTitles();
    }
    
    
    @Override
    public int getRowCount() {
        return data.size();
    }

    @Override
    public int getColumnCount() {
        return titles.size();
    }

    @Override
    public String getColumnName(int column) {
        return titles.get(column);
    }

    @Override
    public boolean isCellEditable(int rowIndex, int columnIndex) {
        return false;
    }

    @Override
    public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
        if (rowIndex < 0 || rowIndex > getRowCount()) {
            throw new IllegalArgumentException();
        }

        if (columnIndex < 0 || columnIndex > getColumnCount()) {
            throw new IllegalArgumentException();
        }

        String methodName = "set" + getColumnName(columnIndex);

        try {
            Method method = data.get(rowIndex).getClass()
                    .getMethod(methodName, aValue.getClass());
            method.invoke(data.get(rowIndex), aValue);
        } catch (IllegalAccessException ex) {
            Logger.getLogger(NewAbstractTableModel.class.getName()).log(
                    Level.SEVERE, ex.getMessage(), ex);
        } catch (IllegalArgumentException ex) {
            Logger.getLogger(NewAbstractTableModel.class.getName()).log(
                    Level.SEVERE, ex.getMessage(), ex);
        } catch (InvocationTargetException ex) {
            Logger.getLogger(NewAbstractTableModel.class.getName()).log(
                    Level.SEVERE, ex.getMessage(), ex);
        } catch (NoSuchMethodException ex) {
            Logger.getLogger(NewAbstractTableModel.class.getName()).log(
                    Level.SEVERE, ex.getMessage(), ex);
        } catch (SecurityException ex) {
            Logger.getLogger(NewAbstractTableModel.class.getName()).log(
                    Level.SEVERE, ex.getMessage(), ex);
        }
    }

    @Override
    public Object getValueAt(int rowIndex, int columnIndex) {
        Object invoke = null;
        try {
            if (rowIndex < 0 || rowIndex > getRowCount()) {
                throw new IllegalArgumentException("rowIndex: " + rowIndex);
            } else if (columnIndex < 0 || columnIndex > getColumnCount()) {
                throw new IllegalArgumentException("columnIndex: " + columnIndex);
            }

            String methodName = "get" + titles.get(columnIndex);

            Method method = data.get(rowIndex).getClass().getMethod(methodName);
            invoke = method.invoke(data.get(rowIndex));
        } catch (IllegalAccessException ex) {
            Logger.getLogger(NewAbstractTableModel.class.getName())
                    .log(Level.SEVERE, ex.getMessage(), ex);
        } catch (IllegalArgumentException ex) {
            Logger.getLogger(NewAbstractTableModel.class.getName())
                    .log(Level.SEVERE, ex.getMessage(), ex);
        } catch (InvocationTargetException ex) {
            Logger.getLogger(NewAbstractTableModel.class.getName())
                    .log(Level.SEVERE, ex.getMessage(), ex);
        } catch (NoSuchMethodException ex) {
            Logger.getLogger(NewAbstractTableModel.class.getName())
                    .log(Level.SEVERE, ex.getMessage(), ex);
        } catch (SecurityException ex) {
            Logger.getLogger(NewAbstractTableModel.class.getName())
                    .log(Level.SEVERE, ex.getMessage(), ex);
        }
        return invoke;
    }

    @Override
    public Class<?> getColumnClass(int columnIndex) {
        //TODO Hacer que devulva el Tipo de columna
        return super.getColumnClass(columnIndex);
    }

    public void updateModel(T[] data) {
        updateModel(Arrays.asList(data));
    }

    public void updateModel(List<T> data) {
        this.data = data;
        fireTableDataChanged();
    }

    private void setTitles() {
        try {
            BeanInfo info = Introspector.getBeanInfo(t, 
                    Introspector.IGNORE_IMMEDIATE_BEANINFO);
            
            for (PropertyDescriptor pd : info.getPropertyDescriptors()) {
                if ((pd.getPropertyType() != Class.class) 
                        && (pd.getPropertyType() != Set.class)) {
                    String title = firstCharToUpperCase(pd.getName());
                    titles.add(title);
                }
            }
        } catch (IntrospectionException ex) {
            Logger.getLogger(NewAbstractTableModel.class.getName())
                    .log(Level.SEVERE, ex.getMessage(), ex);
        }
    }

    private String firstCharToUpperCase(String aString) {
        char c = Character.toUpperCase(aString.charAt(0));
        return c + aString.substring(1);
    }
}